﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UIWindow.Domains;
using UIWindow.Lucifer;

namespace UIWindow.DeviceCommunications
{
    public class CommTCP : ICommunication
    {
        public event CustomDelegates.DropAlarm DropAlarms;
        public List<byte> ByteCache { get; set; }
        private bool _isconnect;
        public bool IsConnect { get { return _isconnect; } }

        private Socket _socket;
        private string _address;
        private int _port;
        private int _timeout;
        private int _index;
        public CommTCP(string address, int port, int index, int outtime = 3000)
        {
            _address = address;
            _port = port;
            _timeout = outtime;
            _index = index;
            ByteCache = new List<byte>();
        }

        public void Close()
        {
            if (_socket != null && _socket.Connected) { 
                _socket.Close();
                _socket = null;
            }
        }

        public bool Connect()
        {
            if (_socket == null || !_socket.Connected)
            {
                try
                {
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _socket.SendTimeout = _timeout;
                    _socket.ReceiveTimeout = _timeout;
                    _socket.ReceiveBufferSize = 2048;
                    _socket.SendBufferSize = 1024;
                    _socket.Connect(IPAddress.Parse(_address), _port);
                    _isconnect = _socket.Connected;
                }
                catch (SocketException)
                {
                    return false;
                }
                return _socket.Connected;
            }
            return false;
        }

        public void Read(CancellationTokenSource tokenSource)
        {
            Task.Run(() =>
            {
                byte[] bytes = new byte[2048];
                while (!tokenSource.Token.IsCancellationRequested && _socket != null)
                {
                    try
                    {
                        int length = _socket.Receive(bytes, 0, 2048, SocketFlags.None);
                        ByteCache.AddRange(bytes.Take(length).ToArray());
                        if (ByteCache.Count > 2048) ByteCache.Clear();
                    }
                    catch (Exception ex)
                    {
                        _isconnect = false;
                        DropAlarms(_address, _port, _index, ex.Message);
                    }
                    Thread.Sleep(50);
                }
            }, tokenSource.Token);
        }

        public byte[] Read()
        {
            try
            {
                byte[] bytes = new byte[2048];
                int length = _socket.Receive(bytes, 0, 2048, SocketFlags.None);
                return bytes.Take(length).ToArray();
            }
            catch (Exception ex)
            {
                _isconnect = false;
                DropAlarms(_address, _port, _index, ex.Message);
                return new byte[0];
            }
        }

        public void SetTimeout(int time)
        {
            _timeout = time;
            if (_socket != null & _socket.Connected)
            {
                _socket.ReceiveTimeout = time;
                _socket.SendTimeout = time;
            }
        }

        public void Write(byte[] bytes)
        {
            try
            {
                _socket.Send(bytes, 0, bytes.Length, SocketFlags.None);
            }
            catch (Exception ex)
            {
                _isconnect = false;
                DropAlarms(_address, _port, _index, ex.Message);
            }
        }

        public bool Write(IProtocol protocol)
        {
            try
            {
                var bytes = protocol.GetStream();
                _socket.Send(bytes, 0, bytes.Length, SocketFlags.None);
                return true;
            }
            catch (Exception ex)
            {
                _isconnect = false;
                DropAlarms(_address, _port, _index, ex.Message);
                return false;
            }
        }

        public T ReadWrite<T>(IProtocol protocol) where T : class, new()
        {
            Write(protocol);
            Thread.Sleep(20);
            byte[] bytes = new byte[2048];
            int length = _socket.Receive(bytes, 0, bytes.Length, SocketFlags.None);
            return (T)(new T() as IProtocol).Handle(bytes.Take(length).ToArray());
        }
    }
}
